package com.jingdianjichi.club.gateway.exception;

import cn.dev33.satoken.exception.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jingdianjichi.club.gateway.entity.Result;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import static org.apache.logging.log4j.message.MapMessage.MapFormat.JSON;

/**
 * 网关全局异常处理器
 * 
 * 该类实现了Spring WebFlux的ErrorWebExceptionHandler接口，用于统一处理
 * Spring Cloud Gateway中发生的各种异常，特别是Sa-Token认证授权异常。
 * 
 * 主要功能：
 * 1. 捕获并处理Sa-Token认证异常（SaTokenException）
 * 2. 处理其他系统异常
 * 3. 统一异常响应格式，返回JSON格式的错误信息
 * 4. 设置合适的HTTP状态码和响应头
 * 
 * 异常处理策略：
 * - Sa-Token异常：返回401状态码，提示"验证失败"
 * - 其他异常：返回500状态码，提示"系统繁忙"
 * 
 * @author ChickenWing
 * @since 1.0.0
 */
@Component
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {

    /**
     * JSON序列化工具
     * 用于将异常响应结果对象转换为JSON字符串
     */
    private ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 全局异常处理方法
     * 
     * 该方法是ErrorWebExceptionHandler接口的核心实现，负责处理网关中发生的所有异常。
     * 方法采用响应式编程模型，返回Mono<Void>类型，适配Spring WebFlux的异步处理机制。
     * 
     * 处理流程：
     * 1. 获取请求和响应对象
     * 2. 根据异常类型设置不同的错误码和消息
     * 3. 构建统一的错误响应格式
     * 4. 设置响应头信息
     * 5. 将错误信息序列化为JSON并写入响应体
     * 
     * @param serverWebExchange 服务器Web交换对象，包含请求和响应信息
     * @param throwable 捕获到的异常对象
     * @return Mono<Void> 响应式编程的空返回值，表示异步处理完成
     */
    @Override
    public Mono<Void> handle(ServerWebExchange serverWebExchange, Throwable throwable) {
        // 获取HTTP请求对象（虽然当前未使用，但可用于日志记录等扩展功能）
        ServerHttpRequest request = serverWebExchange.getRequest();
        // 获取HTTP响应对象，用于设置响应状态码、头信息和响应体
        ServerHttpResponse response = serverWebExchange.getResponse();
        
        // 初始化响应码和错误消息
        int code = 200;
        String msg = "";
        
        // 根据异常类型进行分类处理
        if (throwable instanceof NotLoginException) {
            // Token相关异常处理
            NotLoginException notLoginException = (NotLoginException) throwable;
            // 根据具体的token异常类型设置不同的状态码和消息
            if (NotLoginException.INVALID_TOKEN.equals(notLoginException.getType()) ||
                NotLoginException.TOKEN_TIMEOUT.equals(notLoginException.getType()) ||
                NotLoginException.BE_REPLACED.equals(notLoginException.getType()) ||
                NotLoginException.KICK_OUT.equals(notLoginException.getType())) {
                // Token无效相关异常，返回402状态码
                response.setStatusCode(HttpStatus.PAYMENT_REQUIRED);
                msg = "Token无效或已过期";
                code = 402;
            } else {
                // 其他登录异常（如未提供Token），返回401状态码
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                msg = "未登录或登录状态异常";
                code = 402;
            }
        } else if (throwable instanceof NotRoleException) {
            // 角色校验异常处理
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            NotRoleException notRoleException = (NotRoleException) throwable;
            msg = "权限不足，缺少角色：" + notRoleException.getRole();
            code = 401;
        } else if (throwable instanceof NotPermissionException) {
            // 权限校验异常处理
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            NotPermissionException notPermissionException = (NotPermissionException) throwable;
            msg = "权限不足，缺少权限：" + notPermissionException.getCode();
            code = 401;
        } else if (throwable instanceof DisableServiceException) {
            // 账户被封禁异常处理
            response.setStatusCode(HttpStatus.FORBIDDEN);
            DisableServiceException disableLoginException = (DisableServiceException) throwable;
            msg = "账户已被封禁，" + disableLoginException.getDisableTime() + "秒后解封";
            code = 403;
        } else if (throwable instanceof SaTokenException) {
            // 其他Sa-Token异常处理
            response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            msg = "认证服务异常：" + throwable.getMessage();
            code = 500;
        } else {
            // 其他系统异常处理
            response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            msg = "系统繁忙" + throwable.getMessage();
            code = 500;
        }
        
        // 构建统一的错误响应结果对象
        Result result = Result.fail(code, msg);
        
        // 设置响应头信息
        HttpHeaders headers = response.getHeaders();
        // 设置响应内容类型为JSON格式，编码为UTF-8
        headers.add("Content-Type", "application/json;charset=UTF-8");
        
        // 使用响应式编程方式写入响应体
        return response.writeWith(Mono.fromSupplier(() -> {
            // 获取数据缓冲区工厂，用于创建响应数据缓冲区
            DataBufferFactory dataBufferFactory = response.bufferFactory();
            byte[] bytes = null;
            try {
                // 将结果对象序列化为JSON字节数组
                bytes = objectMapper.writeValueAsBytes(result);
            } catch (JsonProcessingException e) {
                // JSON序列化异常处理（实际项目中建议使用日志框架记录异常）
                e.printStackTrace();
            }
            // 将字节数组包装为数据缓冲区并返回
            return dataBufferFactory.wrap(bytes);
        }));
    }
}
